package org.dba.dbunit;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.pool.impl.GenericObjectPool;

public class wf_Db_Factory {
	private String driver;
	private String url;
	private String user;
	private String password;
	private String schema;
	private int minConnection;
	private int idleTime;
	private int maxConnection;
	private double maxConnTime;
	private GenericObjectPool pool;
	private PoolingDataSource ds;
	
	Log log = LogFactory.getLog(getClass());

	public void load() throws wf_Db_Exception {
		try {
			this.pool = new GenericObjectPool();
			this.pool.setMaxActive(this.maxConnection);
			this.pool.setMaxWait((long) (this.maxConnTime * 1000.0));
			this.pool.setMinIdle(this.minConnection);
			this.pool.setMaxIdle(this.maxConnection);
			this.pool.setMinEvictableIdleTimeMillis(this.idleTime * 60000);
			this.pool.setTimeBetweenEvictionRunsMillis((this.idleTime / 2 + 1) * 60000);
			this.pool.setWhenExhaustedAction((byte) 1);
			Properties props = new Properties();
			props.setProperty("user", this.user);
			props.setProperty("password", this.password);
			ConnectionFactory cf = new DriverConnectionFactory((Driver) Class.forName(this.driver).newInstance(), this.url, props);
			PoolableConnectionFactory factory = new PoolableConnectionFactory(cf, this.pool, null, null, false, false);
			factory.setDefaultAutoCommit(false);
			factory.setDefaultTransactionIsolation(2);
			this.pool.setFactory(factory);
			this.ds = new PoolingDataSource(this.pool);
			this.ds.setAccessToUnderlyingConnectionAllowed(true);
		} catch (RuntimeException e) {
			e.printStackTrace();
			throw new wf_Db_Exception(e.getMessage());
		} catch (Exception e) {
			e.printStackTrace();
			throw new wf_Db_Exception(e.getMessage());
		} catch (Throwable e) {
			e.printStackTrace();
			throw new wf_Db_Exception(e.getMessage());
		}
	}

	public void release() {
		try {
			this.pool.close();
		} catch (RuntimeException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} catch (Throwable e) {
			e.printStackTrace();
		} finally {
			this.ds = null;
			this.pool = null;
		}
	}

	public void load(File configfile) throws wf_Db_Exception {
		Properties properties = new Properties();
		try {
			FileInputStream fileInputStream = new FileInputStream(configfile);
			try {
				properties.load(fileInputStream);
			} finally {
				fileInputStream.close();
			}
		} catch (FileNotFoundException e) {
			throw new wf_Db_Exception(e.getMessage());
		} catch (IOException e) {
			throw new wf_Db_Exception(e.getMessage());
		}
		build(properties);
	}

	public void load(InputStream configfile) throws wf_Db_Exception {
		Properties properties = new Properties();
		try {
			properties.load(configfile);
		} catch (IOException e) {
			log.error(e.getMessage());
		}
		build(properties);
	}

	public void build(Properties properties) throws wf_Db_Exception {
		try {
			this.driver = properties.getProperty("JDBC_DRIVER");
			this.url = properties.getProperty("CONNECTION_URL");
			this.user = properties.getProperty("LOGIN_ID");
			this.password = properties.getProperty("LOGIN_PASSWORD");
			this.schema = properties.getProperty("SCHEMA");
			this.minConnection = Integer.parseInt(properties.getProperty("MIN_CONNECTION"));
			this.maxConnection = Integer.parseInt(properties.getProperty("MAX_CONNECTION"));
			this.maxConnTime = Double.parseDouble(properties.getProperty("MAX_CONNECT_TIME"));
			if (properties.containsKey("IDLETIME"))
				this.idleTime = Integer.parseInt(properties.getProperty("IDLETIME"));
			else {
				this.idleTime = 5;
			}

		} catch (Exception e) {
			throw new wf_Db_Exception(e.getMessage());
		}
		load();
	}

	public String getDriver() {
		return this.driver;
	}

	public wf_Db_Connect get() throws wf_Db_Exception {
		try {
			Connection conn = this.ds.getConnection();
			if (conn.getAutoCommit())
				conn.setAutoCommit(false);
			return new wf_Db_Connect(conn, this);
		} catch (SQLException e) {
			e.printStackTrace();
			throw new wf_Db_Exception(e.getMessage());
		} catch (NullPointerException e) {
			e.printStackTrace();
			throw new wf_Db_Exception(e.getMessage());
		} catch (RuntimeException e) {
			e.printStackTrace();
			throw new wf_Db_Exception(e.getMessage());
		} catch (Exception e) {
			e.printStackTrace();
			throw new wf_Db_Exception(e.getMessage());
		} catch (Throwable e) {
			e.printStackTrace();
			throw new wf_Db_Exception(e.getMessage());
		}
	}

	public void free(wf_Db_Connect t_connect) {
		try {
			Connection connect = t_connect.getConnect();
			if (connect != null)
				try {
					t_connect.Shutdown();
				} finally {
					connect.close();
				}
		} catch (RuntimeException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	public int getMaxConnection() {
		return this.maxConnection;
	}

	public void setMaxConnection(int maxConnection) {
		this.maxConnection = maxConnection;
	}

	public double getMaxConnTime() {
		return this.maxConnTime;
	}

	public void setMaxConnTime(double maxConnTime) {
		this.maxConnTime = maxConnTime;
	}

	public int getMinConnection() {
		return this.minConnection;
	}

	public void setMinConnection(int minConnection) {
		this.minConnection = minConnection;
	}

	public String getPassword() {
		return this.password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getUrl() {
		return this.url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUser() {
		return this.user;
	}

	public void setUser(String user) {
		this.user = user;
	}

	public void setDriver(String driver) {
		this.driver = driver;
	}

	public String getDatabaseDrv() {
		return this.driver;
	}


	public String getDatabaseSchema() {
		if (this.schema != null)
			return this.schema;
		return this.user;
	}

	public DataSource getDataSource() {
		return this.ds;
	}

}